﻿using mozhi.smarterp.Business.Purchases.Repositories;
using Mozhi.Abp.Domain.AuthorCenter;
using Mozhi.Abp.Domain.BasicData.Repositories;
using Mozhi.Abp.Domain.BasicData;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using mozhi.smarterp.Business.Purchases.Services;
using mozhi.smarterp.Business.Enums;
using mozhi.smarterp.Business.Payments.Repositories;

namespace mozhi.smarterp.Business.Purchases.Order
{
    public class PurchaseOrderAppService : BusinessAppService, IPurchaseOrderAppService
    {
        private IPurchaseOrderRepository _repository;
        private IProductRepository _productRepository;
        private IDepartmentRepository _departmentRepository;
        private IEmployeeRepository _employeeRepository;
        private ITaxRateRepository _taxRateRepository;
        private ICurrencyRepository _currencyRepository;
        private IUnitRepository _unitRepository;
        private ISupplierRepository _supplierRepository;
        private PurchaseOrderManager _manager;
        private IPurchaseReceiptRepository _receiptRepository;
        private IPurchaseOrderReceiptRepository _orderReceiptRepository;
        private IPaymentRequisitonRepository _paymentRequisitonRepository;

        public PurchaseOrderAppService(IPurchaseOrderRepository repository , ICurrencyRepository currencyRepository, IProductRepository productRepository,
            IDepartmentRepository departmentRepository, IEmployeeRepository employeeRepository, ITaxRateRepository taxRateRepository, IUnitRepository unitRepository,
            ISupplierRepository supplierRepository , PurchaseOrderManager manager ,
            IPurchaseReceiptRepository receiptRepository , IPurchaseOrderReceiptRepository orderReceiptRepository , IPaymentRequisitonRepository paymentRequisitonRepository)
        {
            _repository = repository;
            _currencyRepository = currencyRepository;
            _unitRepository = unitRepository;
            _supplierRepository = supplierRepository;
            _currencyRepository = currencyRepository;
            _productRepository = productRepository;
            _departmentRepository = departmentRepository;
            _employeeRepository = employeeRepository;
            _taxRateRepository = taxRateRepository;
            _manager = manager;
            _receiptRepository = receiptRepository;
            _orderReceiptRepository = orderReceiptRepository;
            _paymentRequisitonRepository = paymentRequisitonRepository;
        }

        public async Task<PurchaseOrderDto> CreateAsync(PurchaseOrderCreateDto input)
        {
            var order = await _manager.GetAysnc(null, input.DepartmentId, input.Date, input.RequiredDate, null, input.Comment, input.Type, input.PaymentTermId, input.CurrencyId, input.ExchangeRate,
               input.IncludeTax, input.PurchaserId, input.SupplierId);

            input.Items = input.Items ?? new List<PurchaseOrderItemCreateDto>();

            var orderItems = new List<PurchaseOrderItem>();

            foreach (var item in input.Items)
            {
                var orderItem = await _manager.GetItemsAsync(null, item.ProductId, item.PurchaseUnitId, item.PurchaseQuantity, item.Price ?? 0, item.IsGift, item.TaxRateId, item.Remark);

                orderItems.Add(orderItem);
            }

            order.ChangeItems(orderItems);


            order = await _repository.InsertAsync(order);

            var result = ObjectMapper.Map<PurchaseOrder, PurchaseOrderDto>(order);

            await LoadOtherInfoAsync(new List<PurchaseOrderDto> { result });

            return result;
        }

        public async Task DeleteAsync(Guid id)
        {
            await _manager.DeleteAsync(id);
        }

        public async Task<PurchaseOrderDto> GetAsync(Guid id)
        {
            var order = await _repository.GetAsync(id);

            var result = ObjectMapper.Map<PurchaseOrder, PurchaseOrderDto>(order);

            await LoadOtherInfoAsync(new List<PurchaseOrderDto> { result });

            return result;
        }

        public async Task<PagedResultDto<PurchaseOrderDto>> GetListAsync(PurchaseOrderResultRequestDto filter)
        {
            var orders = await _repository.GetPagedListAsync(filter.Number, filter.StartDate, filter.EndDate, filter.Status, filter.Type , filter.SkipCount, filter.MaxResultCount, filter.Sorting, true);

            var count = await _repository.GetCountAsync(filter.Number, filter.StartDate, filter.EndDate, filter.Status, filter.Type);

            var result = new PagedResultDto<PurchaseOrderDto>()
            {
                Items = ObjectMapper.Map<List<PurchaseOrder>, List<PurchaseOrderDto>>(orders),
                TotalCount = count
            };

            await LoadOtherInfoAsync(result.Items.ToList());

            return result;
        }

        public async Task<PurchaseOrderDto> UpdateAsync(PurchaseOrderUpdateDto input)
        {
            var order = await _manager.GetAysnc(input.Id, input.DepartmentId, input.Date, input.RequiredDate ,null, input.Comment, input.Type, input.PaymentTermId , input.CurrencyId, input.ExchangeRate,
                input.IncludeTax, input.PurchaserId , input.SupplierId);

            input.Items = input.Items ?? new List<PurchaseOrderItemUpdateDto>();
            input.AdvancePaymentItems = input.AdvancePaymentItems ?? new List<PurchaseOrderAdvancePaymentUpdateDto>();

            var orderItems = new List<PurchaseOrderItem>();

            foreach (var item in input.Items)
            {
                var orderItem = await _manager.GetItemsAsync(input.Id, item.ProductId, item.PurchaseUnitId, item.PurchaseQuantity, item.Price ?? 0, item.IsGift , item.TaxRateId, item.Remark);

                orderItems.Add(orderItem);
            }

            order.ChangeItems(orderItems);

            var advancePaymentItems = new List<PurchaseOrderAdvancePaymentItem>();

            foreach (var item in input.AdvancePaymentItems)
            {
                var advancePayment = await _manager.GetAdvancePaymentItemAsync(item.Id, item.AdvancePaymentPercent, item.DueDate, item.ExpectedDate);

                advancePaymentItems.Add(advancePayment);
            }

            order.SetAdvancePaymentItems(advancePaymentItems);

            order = await _repository.UpdateAsync(order);

            var result = ObjectMapper.Map<PurchaseOrder, PurchaseOrderDto>(order);

            await LoadOtherInfoAsync(new List<PurchaseOrderDto> { result });

            return result;
        }

        public async Task UpdateStatusAsync(Guid id, DocumentStatusType status)
        {
            var order = await _manager.UpdateStatusAsync(id, status);

            await _repository.UpdateAsync(order);
        }

        public async Task<Guid> PushReceiptAsync(Guid id)
        {
            var receipt = await _manager.PushPurchaseReceiptAsync(id);

            return receipt.Id;
        }

        public async Task<Guid> PushPaymentRequisitionAsync(Guid id)
        {
            var result = await _manager.PushPaymentRequisitionAsync(id);

            result = await _paymentRequisitonRepository.InsertAsync(result);

            return result.Id;
        }

        #region
        private async Task LoadOtherInfoAsync(List<PurchaseOrderDto> orders)
        {
            if (orders == null || orders.Count == 0)
                return;

            foreach (var order in orders)
            {
                var currency = await _currencyRepository.GetAsync(order.CurrencyId);
                var department = await _departmentRepository.GetAsync(order.DepartmentId.Value);
                var purchaser = await _employeeRepository.GetAsync(order.PurchaserId.Value);
                var supplier = await _supplierRepository.GetAsync(order.SupplierId.Value);

                order.CurrencyName = currency?.Name;
                order.DepartmentName = department?.Name;
                order.PurchaserName = purchaser?.Name;
                order.SupplierName = supplier?.Name;

                foreach (var item in order.Items)
                {
                    var product = await _productRepository.GetAsync(item.ProductId);
                    var taxRate = item.TaxRateId.HasValue ? await _taxRateRepository.GetAsync(item.TaxRateId.Value) : null;
                    var requisitionUnit = await _unitRepository.GetAsync(item.PurchaseUnitId);
                    var basicUnit = await _unitRepository.GetAsync(item.BasicUnitId);

                    item.ProductCode = product?.Code;
                    item.ProductName = product?.Name;
                    item.ProductModel = product?.Model;
                    item.TaxRateName = taxRate?.Name;
                    item.PurchaseUnitName = requisitionUnit?.Name;
                    item.BasicUnitName = basicUnit?.Name;

                }
            }
        }
        #endregion
    }
}
